/*
 * Copyright (c)  2019. houbinbin Inc.
 * heaven All rights reserved.
 */

package com.github.houbb.id.core;


import com.github.houbb.heaven.annotation.ThreadSafe;
import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.heaven.util.lang.ThreadUtil;
import com.github.houbb.heaven.util.util.DateUtil;
import com.github.houbb.id.api.Id;
import com.github.houbb.id.exception.IdException;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

/**
 * <p> 对人类阅读友好的算法 </p>
 *
 * <pre> Created: 2018/6/15 上午11:41  </pre>
 * <pre> Project: id  </pre>
 *
 * 00-xxx.xxx-yyMMddHHhhssSSS-000
 *
 * 00 代表系统编号
 * xxx.xxx 6 最后6位
 * yyMMddHHhhssSSS 时间戳
 * 000 最后的随机三位数
 *
 * 1. 暂时不考虑时钟回拨问题
 * 2. 是否保证严格递增？
 * @author houbinbin
 * @since 0.0.4
 */
@ThreadSafe
public class IdHuman implements Id {

    /**
     * 系统编号
     *
     * 每一个应用都应该有一个系统标识，暂定为2位
     * @since 0.0.4
     */
    private final String sysCode;

    /**
     * 机器 ip
     *
     * 默认选择 ip 的低 16 位
     * @since 0.0.4
     */
    private final String ip;

    /**
     * 是否严格递增
     *
     * 默认保证严格递增
     * @since 0.0.4
     */
    private final boolean strictlyIncrease;

    /**
     * 最后的时间戳
     * @since 0.0.4
     */
    private volatile String lastTimeStamp;

    /**
     * 存放最后 3 位的标识
     * @since 0.0.4
     */
    private static final Queue<String> QUEUE = new ConcurrentLinkedQueue<>();

    public IdHuman() {
        this("00", getDefaultIp(), true);
    }

    public IdHuman(String sysCode, String ip, boolean strictlyIncrease) {
        this.sysCode = sysCode;
        this.ip = ip;
        this.strictlyIncrease = strictlyIncrease;

        // 初始化队列
        fillQueue();
    }

    @Override
    public synchronized String id() {
        String random = getRandom();
        String timeStamp = getTimeStamp();
        this.lastTimeStamp = timeStamp;
        return this.sysCode+this.ip+timeStamp+random;
    }

    /**
     * 获取随机信息
     *
     * 1. 保证数据的在 1ms 内是严格递增的。
     *
     * 如果
     * @return 随机信息
     * @since 0.0.4
     */
    protected String getRandom() {
        String random = QUEUE.poll();

        // 如果元素为空，则重新初始化队列
        if(StringUtil.isEmptyTrim(random)) {
            fillQueue();

            // 在队列填充完成之后获取，节约一点时间。
            // 避免同一毫秒内，id 下降的情况
            if(strictlyIncrease) {
                String currentTimeStamp = getTimeStamp();
                if(currentTimeStamp.equals(lastTimeStamp)) {
                    DateUtil.sleep(1);
                }
            }

            random = QUEUE.poll();
        }
        return random;
    }

    /**
     * 填充队列
     * @since 0.0.4
     */
    private void fillQueue() {
        for(int i = 0; i < 1000; i++) {
            String value = StringUtil.leftPadding(String.valueOf(i), 3);
            QUEUE.add(value);
        }
    }

    /**
     * 获取当前的时间戳
     *
     * 1. 17 位
     * 2. 15 位
     * 3. linux 时间戳
     * @return 时间戳
     * @since 0.0.4
     */
    protected String getTimeStamp() {
        return DateUtil.getCurrentTimeStampStr15();
    }

    /**
     * 获取默认的 ip 信息
     * 192.168.56.1
     *
     * 同一个应用的机器组一般只需要关注最后的 56.1 即可
     * 为了对齐，全部左补0为6位。
     *
     * 056001
     * @return ip 信息
     * @since 0.0.4
     */
    private static String getDefaultIp() {
        try {
            InetAddress address = InetAddress.getLocalHost();
            String hostAddress = address.getHostAddress();
            String[] ips = hostAddress.split("\\.");
            String three = StringUtil.leftPadding(ips[2], 3);
            String four = StringUtil.leftPadding(ips[3], 3);
            return three+four;
        } catch (UnknownHostException e) {
            throw new IdException(e);
        }
    }

}
